package tungsten.pb;

import com.google.protobuf.GeneratedMessage;
import com.google.protobuf.Message;
import io.netty.channel.ChannelHandlerContext;
import org.apache.log4j.Logger;
import tungsten.base.server.BaseClient;
import tungsten.base.server.MessageHandler;
import tungsten.base.utils.ClassUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by Lv on 2016/6/17.
 */
public class PBClient extends BaseClient implements MessageHandler
{

    private static Logger logger = Logger.getLogger(PBClient.class);

    private static Map<Class<?>, PBMessageEncoder> encoderMap = new HashMap<>();
    /**
     * 初始化所有SetMethod
     */
    public static void initialize() throws Exception
    {
        //拿所有的Builder
        List<Class<GeneratedMessage.Builder>> builders = ClassUtils.getClassList(PBClient.class.getPackage().getName(), true, null, GeneratedMessage.Builder.class,false);
        //拿所有的Message
        List<Class<GeneratedMessage>> messages = ClassUtils.getClassList(PBClient.class.getPackage().getName(), true, null, GeneratedMessage.class,false);

        //取得模块对应的ID
        Map<String,Integer> modules = new HashMap<>();
        for (Class<GeneratedMessage> message : messages) {
            String name = message.getName();
            if (name.indexOf("$ModuleType") > 0)
            {
                modules.put(name.split("\\$")[0],message.getField("TYPE_FIELD_NUMBER").getInt(message));
            }
        }

        Map<Class<?>,Class<?>> returnMap = new HashMap<>();
        for (Class<GeneratedMessage> message : messages)
        {
            returnMap.put(message.getMethod("newBuilder").getReturnType(),message);
        }

        for (Class<GeneratedMessage.Builder> builder : builders)
        {
            Method[] methods = builder.getDeclaredMethods();
            for (Method method : methods)
            {
                Parameter[] parameters = method.getParameters();

                if (parameters.length != 1) continue;

                if (!method.getName().startsWith("set")) continue;

                Class<?> parameter = method.getParameterTypes()[0];
                if (Message.class.isAssignableFrom(parameter))
                {
                    String name = builder.getName().split("\\$")[0];
                    int moduleId = modules.get(name);
                    encoderMap.put(method.getParameterTypes()[0], new PBMessageEncoder(returnMap.get(builder),returnMap.get(builder).getMethod("newBuilder"),method,moduleId));
                }
            }
        }
    }

    public PBClient(ChannelHandlerContext ctx) {
        super(ctx);
    }

    @Override
    protected MessageHandler getMessageHandler() {
        return this;
    }

    @Override
    public Object handle(Object object)
    {
        PBMessageEncoder encoder = encoderMap.get(object.getClass());
        if (encoder != null)
        {
            try {
                Message.Builder builder = null;
                builder = (Message.Builder) encoder.getBuildMethod().invoke(encoder.getClazz());
                encoder.getSetMethod().invoke(builder, object);
                return new PBMessage(encoder.getModuleId(),builder.build());
            } catch (Exception e) {
                logger.error("",e);
            }
        } else {
            logger.error("can't encoder : " + object);
        }
        return null;
    }
}
